/*
 Copyright (c) 2009, Éfren Lopes de Souza
                     Universidade Federal do Amazonas
                     Manaus, AM - Brazil
                     dcc.ufam.edu.br

 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:

 - Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

 - Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the
   distribution.

 - Neither the name 'Sinalgo' nor the names of its contributors may be
   used to endorse or promote products derived from this software
   without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package projects.ufam;

import java.awt.Color;
import java.awt.Graphics;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Random;
import java.util.logging.Logger;

import projects.defaultProject.models.energyConsumptionModels.InfinitePowerSupply;
import projects.defaultProject.models.mobilityModels.RandomWayPoint;
import projects.defaultProject.nodes.nodeImplementations.LocalizableNode;
import projects.ufam.nodes.messages.TrackingMsg;
import projects.ufam.nodes.nodeImplementations.BroadcastNode;
import projects.ufam.nodes.nodeImplementations.Target;
import sinalgo.configuration.Configuration;
import sinalgo.configuration.CorruptConfigurationEntryException;
import sinalgo.gui.transformation.PositionTransformation;
import sinalgo.nodes.Node;
import sinalgo.nodes.NodeState;
import sinalgo.nodes.Position;
import sinalgo.runtime.AbstractCustomGlobal;
import sinalgo.runtime.Global;
import sinalgo.tools.Tools;
import sinalgo.tools.logging.Logging;
import sinalgo.tools.statistics.GaussianDistribution;

/**
 * This class holds customized global state and methods for the framework. The
 * only mandatory method to overwrite is <code>hasTerminated</code> <br>
 * Optional methods to override are
 * <ul>
 * <li><code>customPaint</code></li>
 * <li><code>handleEmptyEventQueue</code></li>
 * <li><code>onExit</code></li>
 * <li><code>preRun</code></li>
 * <li><code>preRound</code></li>
 * <li><code>postRound</code></li>
 * <li><code>checkProjectRequirements</code></li>
 * </ul>
 * 
 * @see sinalgo.runtime.AbstractCustomGlobal for more details. <br>
 *      In addition, this class also provides the possibility to extend the
 *      framework with custom methods that can be called either through the menu
 *      or via a button that is added to the GUI.
 */
public class CustomGlobal extends AbstractCustomGlobal {
	/**
	 * Length of a round in ms (for speed conversions)
	 */
	public static int roundLength = 2;
	public static int kCoverage = 0;
	public static Node sink;
	public static Target target = null;
	public static double sensorRange;
	public static java.util.Random rand = sinalgo.tools.Tools
			.getRandomNumberGenerator();
	private static final int ONE_BEACONS_STRUCTURE = 1;
	private static final int TWO_BEACONS_STRUCTURES = 2;
	private static final int THREE_BEACONS_STRUCTURES = 3;
	private static final int FOUR_BEACONS_STRUCTURES = 4;
	private static final int FIVE_BEACONS_STRUCTURES = 5;
	public static boolean targetEnabled = true, paintTrace = false,
			useResidualOnDC = true;
	private static int beaconsStructures = 1;
	private static int beaconsByStructure = 4;
	private static int beaconsStructureDistanceFromCentralPoint;

	private static double percentageBeacons = 0.15;
	private static int minReferences = 3;
	private static int maxReferences = 10;
	public static double maxResidual;
	private static double noiseMean = 0.1;
	private static double noiseVariance = 0.01;

	private static boolean terminated;
	private static int numberMessages;
	public static String localizationAlgorithm = null;
	public static String densityControlAlgorithm = "GAF";
	public static double densityControlRoundLength = 2000;
	public static int aodvExpirationTime = 200;
	public static int simulationLength = 100000;
	public static String mobilityModel = RandomWayPoint.class.getName();
	static Date date = new Date();
	static int count = 0;
	static SimpleDateFormat fmt = new SimpleDateFormat("ddMMyyyyHHmmss");
	/**
	 * Utilizado para evitar conflito no nome de arquivos em simulacoes que
	 * ocorrem em mais de uma thread
	 */
	static String randomString = "";
	static {
		Position pos = new Position();
		pos.xCoord = rand.nextDouble() * (double) Configuration.dimX;
		pos.yCoord = rand.nextDouble() * (double) Configuration.dimY;
		// Read the mobility model that will be used for the target.
		try {
			mobilityModel = Configuration
					.getStringParameter("target/MobilityModel");
		} catch (CorruptConfigurationEntryException e) {
		}
		// Estava usando o mesmo seed para gerar os nomes randômicos...
		randomString = "" + (new Random()).nextLong();
		target = new Target(pos);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see runtime.AbstractCustomGlobal#hasTerminated()
	 */
	public boolean hasTerminated() {
		return getTerminated();// return false;
	}

	public void preRunDPE(boolean makeBeacon) {
		// A simulação nao deve terminar
		terminated = false;
		numberMessages = -1;

		// Carregando as configurações
		try {
			beaconsStructures = Configuration
					.getIntegerParameter("DPE/BeaconsStructures/number");
		} catch (CorruptConfigurationEntryException e) {
			beaconsStructures = 1;
		}
		try {
			beaconsByStructure = Configuration
					.getIntegerParameter("DPE/BeaconsByStructure/number");
		} catch (CorruptConfigurationEntryException e) {
			beaconsByStructure = 4;
		}
		try {
			beaconsStructureDistanceFromCentralPoint = Configuration
					.getIntegerParameter("DPE/BeaconsStructureDistanceFromCentralPoint/number");
		} catch (CorruptConfigurationEntryException e) {
			beaconsStructureDistanceFromCentralPoint = 10;
		}
		try {
			maxResidual = Configuration
					.getDoubleParameter("DPE/MaxResidual/number");
		} catch (CorruptConfigurationEntryException e) {
			maxResidual = 0.1;
		}
		try {
			noiseMean = Configuration
					.getDoubleParameter("Noise/Mean/percentage");
		} catch (CorruptConfigurationEntryException e) {
			noiseMean = 0.1;
		}
		try {
			noiseVariance = Configuration
					.getDoubleParameter("Noise/Variance/percentage");
		} catch (CorruptConfigurationEntryException e) {
			noiseVariance = 0.01;
		}
		// Definindo as estruturas de beacons
		Position recursionsOrigins[] = null;
		int xmin = 0;
		int ymin = 0;
		int xmax = Configuration.dimX;
		int ymax = Configuration.dimY;
		if (beaconsStructures == ONE_BEACONS_STRUCTURE)
			recursionsOrigins = recursionOriginForOneBeaconsStructure(xmin,
					xmax, ymin, ymax);
		else if (beaconsStructures == TWO_BEACONS_STRUCTURES)
			recursionsOrigins = recursionOriginForTwoBeaconsStructures(xmin,
					xmax, ymin, ymax);
		else if (beaconsStructures == THREE_BEACONS_STRUCTURES)
			recursionsOrigins = recursionOriginForThreeBeaconsStructures(xmin,
					xmax, ymin, ymax);
		else if (beaconsStructures == FOUR_BEACONS_STRUCTURES)
			recursionsOrigins = recursionOriginForFourBeaconsStructures(xmin,
					xmax, ymin, ymax);
		else if (beaconsStructures == FIVE_BEACONS_STRUCTURES)
			recursionsOrigins = recursionOriginForFiveBeaconsStructures(xmin,
					xmax, ymin, ymax);

		// int numberNodes = Tools.getNodeList().size();
		int numberBeacons = (int) Math.round(beaconsStructures
				* beaconsByStructure);

		int b = 0;
		LocalizableNode[] beacons = new LocalizableNode[numberBeacons];
		for (Iterator<Node> i = Tools.getNodeList().iterator(); b < numberBeacons; b++)
			beacons[b] = (LocalizableNode) i.next();

		b = 0;
		for (int i = 0; i < recursionsOrigins.length; i++) {
			double cx = recursionsOrigins[i].xCoord;
			double cy = recursionsOrigins[i].yCoord;
			double scale = 360 / beaconsByStructure;
			double angle = 0;
			for (int j = 0; j < beaconsByStructure; j++) {
				double x = cx
						+ (Math.sin(Math.toRadians(angle)) * beaconsStructureDistanceFromCentralPoint());
				double y = cy
						+ (Math.cos(Math.toRadians(angle)) * beaconsStructureDistanceFromCentralPoint());
				LocalizableNode nodeTmp = beacons[b++];
				nodeTmp.setPosition(new Position(x, y, 0));
				nodeTmp.setRecursionOrigin(new Position(cx, cy, 0));
				if (makeBeacon)
					nodeTmp.makeBeacon();
				angle += scale;
			}
		}
	}

	public void preRun() {
		String temp = null;
		try {
			sensorRange = Configuration.getDoubleParameter("sensorRange");
		} catch (CorruptConfigurationEntryException e) {
			sensorRange = 20;
		}
		try {
			roundLength = Configuration.getIntegerParameter("roundLength");
		} catch (CorruptConfigurationEntryException e) {
			roundLength = 10;
		}
		try {
			temp = Configuration
					.getStringParameter("Localization/Algorithm/name");
			if (temp != null)
				localizationAlgorithm = temp;
		} catch (CorruptConfigurationEntryException e) {
			System.out
					.println("Localization algorithm not defined. Using real positions.");
		}
		try {
			temp = Configuration
					.getStringParameter("DensityControl/Algorithm/name");
			if (temp != null)
				densityControlAlgorithm = temp;

		} catch (CorruptConfigurationEntryException e) {
		}
		try {
			targetEnabled = Configuration
					.getBooleanParameter("tracking/targetenabled");
		} catch (CorruptConfigurationEntryException e) {
			targetEnabled = true;
		}
		try {
			paintTrace = Configuration
					.getBooleanParameter("tracking/paintTraceo");
		} catch (CorruptConfigurationEntryException e) {
			paintTrace = false;
		}
		try {
			useResidualOnDC = Configuration
					.getBooleanParameter("DC/useResidual");
		} catch (CorruptConfigurationEntryException e) {
			useResidualOnDC = false;
		}
		try {
			densityControlRoundLength = Configuration
					.getDoubleParameter("DensityControl/RoundLength");
		} catch (CorruptConfigurationEntryException e) {
			System.out.println("Density control round length not defined.");
		}
		try {
			simulationLength = Configuration
					.getIntegerParameter("simulationLength");
		} catch (CorruptConfigurationEntryException e) {
			System.out.println("Simulation length not defined.");
			simulationLength = 100000;
		}

		try {
			aodvExpirationTime = Configuration
					.getIntegerParameter("aodvExpirationTime");
		} catch (CorruptConfigurationEntryException e) {
			aodvExpirationTime = (int) (densityControlRoundLength / 5);
		}

		try {
			kCoverage = Configuration.getIntegerParameter("Coverage/k");
			// Só pode ser maior que 1
			if (kCoverage < 1)
				kCoverage = 3;
		} catch (CorruptConfigurationEntryException e) {
			kCoverage = 3;
		}

		if (Tools.getNodeList().size() == 0)
			return;
		// Cria o nó sink - o último para que ele não seja colocado no meio,
		// como o DPE
		sink = Tools.getNodeByID(Tools.getNodeList().size() - 1);
		sink.setEnergyConsumptionModel(new InfinitePowerSupply());
		sink.setPosition(10.0, 10.0, 0.0);
		if (sink instanceof LocalizableNode) {
			((LocalizableNode) sink).setSink(true);
			// Preciso fazer a cruz em todos os exprimentos para ser justo
			preRunDPE(false);
		}

		if (Tools.getNodeList().iterator().next() instanceof BroadcastNode)
			preRunBroadcastNode();

		else {
			if (localizationAlgorithm == null) {
				for (Node node : Tools.getNodeList()) {
					((LocalizableNode) node).useRealPosition();
				}
			} else if (localizationAlgorithm.equals("DPE")) {
				preRunDPE(true);
			} else if (localizationAlgorithm.equals("RPE"))
				preRunRPE();
		}
	}

	public void preRunBroadcastNode() {
		terminated = false;
		numberMessages = -1;
	}

	public void preRunRPE() {
		// A simulação nao deve terminar
		terminated = false;
		numberMessages = -1;
		// Carregando as configurações
		try {
			percentageBeacons = Configuration
					.getDoubleParameter("RPE/Beacons/percentage");
		} catch (Exception e) {
			e.printStackTrace();
			percentageBeacons = 0.05;
		}
		try {
			minReferences = Configuration
					.getIntegerParameter("RPE/MinReferences/number");
		} catch (CorruptConfigurationEntryException e) {
			e.printStackTrace();
			minReferences = 4;
		}
		try {
			maxReferences = Configuration
					.getIntegerParameter("RPE/MaxReferences/number");
		} catch (CorruptConfigurationEntryException e) {
			e.printStackTrace();
			maxReferences = 100;
		}
		try {
			maxResidual = Configuration
					.getDoubleParameter("RPE/MaxResidual/number");
		} catch (CorruptConfigurationEntryException e) {
			maxResidual = 1;
		}
		try {
			noiseMean = Configuration
					.getDoubleParameter("Noise/Mean/percentage");
		} catch (CorruptConfigurationEntryException e) {
			e.printStackTrace();
			noiseMean = 0;
		}
		try {
			noiseVariance = Configuration
					.getDoubleParameter("Noise/Variance/percentage");
		} catch (CorruptConfigurationEntryException e) {
			e.printStackTrace();
			noiseVariance = 0.1;
		}

		// Definindo os beacons
		int numberNodes = Tools.getNodeList().size();
		int numberBeacons = (int) Math.ceil(numberNodes * percentageBeacons);
		for (Node node : Tools.getNodeList()) {
			if (node instanceof LocalizableNode) {
				if (node.ID < 4)
					continue;
				if (numberBeacons == 0)
					break;
				((LocalizableNode) node).makeBeacon();
				numberBeacons--;
			}
		}
	}

	public void postRound() {
		double total = 0;
		String className = null;
		int[] coverage = new int[100];
		double x = 0;
		double y = 0;
		double w = Math.sqrt(coverage.length);
		Position p = new Position();
		double totalCovered = 0;
		className = Tools.getNodeList().iterator().next().getClass()
				.getSimpleName();
		int enabled = 0;
		for (int i = 0; i < coverage.length; i++) {
			coverage[i] = 0;
			x = w / 2 + (i % (int) w) * Configuration.dimX / w;
			y = w / 2 + (i / (int) w) * Configuration.dimY / w;
			p.xCoord = x;
			p.yCoord = y;
			for (Node node : Tools.getNodeList()) {
				if (((LocalizableNode) node).isFreeNode())
					continue;
				if (p.distanceTo(node.getPosition()) <= CustomGlobal.sensorRange
						&& node.getState() == NodeState.Enabled) {
					coverage[i]++;
				}

			}

			if (coverage[i] >= kCoverage)
				totalCovered++;
		}
		for (Node node : Tools.getNodeList()) {
			total += node.getEnergyLevel();
			if (node.getState() == NodeState.Enabled)
				enabled++;
		}
		Logging l = Logging.getLogger("dc-energy-"
				+ randomString
				+ className
				+ Tools.getNodeList().size()
				+ "-k"
				+ kCoverage
				+ "-"
				+ CustomGlobal.useResidualOnDC
				+ "-"
				+ (localizationAlgorithm == null ? "real"
						: localizationAlgorithm) + fmt.format(date), true);
		Logging l2 = Logging.getLogger("dc-delivery-"
				+ randomString
				+ className
				+ Tools.getNodeList().size()
				+ "-k"
				+ kCoverage
				+ "-"
				+ CustomGlobal.useResidualOnDC
				+ "-"
				+ (localizationAlgorithm == null ? "real"
						: localizationAlgorithm) + fmt.format(date), true);
		Logging l3 = Logging.getLogger("dc-coverage-"
				+ randomString
				+ className
				+ Tools.getNodeList().size()
				+ "-k"
				+ kCoverage
				+ "-"
				+ CustomGlobal.useResidualOnDC
				+ "-"
				+ (localizationAlgorithm == null ? "real"
						: localizationAlgorithm) + fmt.format(date), true);
		Logging l4 = Logging.getLogger("dc-enabled-"
				+ randomString
				+ className
				+ Tools.getNodeList().size()
				+ "-k"
				+ kCoverage
				+ "-"
				+ CustomGlobal.useResidualOnDC
				+ "-"
				+ (localizationAlgorithm == null ? "real"
						: localizationAlgorithm) + fmt.format(date), true);
		Logging l5 = Logging.getLogger("dc-tracking-"
				+ randomString
				+ className
				+ Tools.getNodeList().size()
				+ "-k"
				+ kCoverage
				+ "-"
				+ CustomGlobal.useResidualOnDC
				+ "-"
				+ (localizationAlgorithm == null ? "real"
						: localizationAlgorithm) + fmt.format(date), true);

		Logging l6 = Logging.getLogger("dc-sent-"
				+ randomString
				+ className
				+ Tools.getNodeList().size()
				+ "-k"
				+ kCoverage
				+ "-"
				+ CustomGlobal.useResidualOnDC
				+ "-"
				+ (localizationAlgorithm == null ? "real"
						: localizationAlgorithm) + fmt.format(date), true);

		l.logln(Global.currentTime + " " + total
				/ (double) Tools.getNodeList().size());

		if (TrackingMsg.getSent() > 0)
			l2.logln(Global.currentTime + " "
					+ ((double) TrackingMsg.getReceived())
					/ ((double) (TrackingMsg.getSent())));
		double pct = (double) totalCovered / (double) coverage.length;
		l3.logln(Global.currentTime + " " + pct);
		l4.logln(Global.currentTime + " "
				+ ((double) enabled / (double) Tools.getNodeList().size()));
		if (Global.currentTime == 150) {
			for (Node node : Tools.getNodeList()) {
				logLocalizationError((LocalizableNode) node);
			}
		}

		if (Global.currentTime % 1000 == 0) {
			diferencaRastreamento(l5);
		}
		l6.logln(Global.currentTime + " " + Global.numberOfMessagesInThisRound);
		if (pct < 0.6) {
			count++;
		} else
			count = 0;
		if (count == densityControlRoundLength
				|| Global.currentTime == simulationLength) {
			diferencaRastreamento(l5);
			System.out.println("Simulation time: "
					+ ((new Date()).getTime() - date.getTime()));
			Tools.exit();

		}
	}

	private static void diferencaRastreamento(Logging l5) {
		for (int i = ((int) Global.currentTime - 1000) / 100; i < Global.currentTime / 100; i++) {
			if (i < 0)
				continue;
			if (target.getTraceEstimation()[i] != null) {
				l5.logln(i
						* 100
						+ " "
						+ target.getTrace()[i].distanceTo(target
								.getTraceEstimation()[i])
						+ " "
						+ target.getTrace()[i].distanceTo(target
								.getTraceMeasurement()[i]) + " "
						+ target.getTrace()[i].xCoord + " "
						+ target.getTrace()[i].yCoord + " "
						+ target.getTraceEstimation()[i].xCoord + " "
						+ target.getTraceEstimation()[i].yCoord + " "
						+ target.getTraceMeasurement()[i].xCoord + " "
						+ target.getTraceMeasurement()[i].yCoord);

			} else {
				l5.logln(i * 100 + " -1.0 -1.0 " + target.getTrace()[i].xCoord
						+ " " + target.getTrace()[i].yCoord
						+ " -1.0 -1.0 -1.0 -1.0");
			}
		}
	}

	public static void logLocalizationError(LocalizableNode node) {
		if (node.getEstimatedPosition() == null)
			return;
		Logging l7 = Logging.getLogger("loc-error-"
				+ randomString
				+ node.getClass().getSimpleName()
				+ Tools.getNodeList().size()
				+ "-k"
				+ kCoverage
				+ "-"
				+ CustomGlobal.useResidualOnDC
				+ "-"
				+ (localizationAlgorithm == null ? "real"
						: localizationAlgorithm) + fmt.format(date), true);
		l7.logln(node.ID + " " + node.getPosition().xCoord + " "
				+ node.getPosition().yCoord + " "
				+ node.getEstimatedPosition().xCoord + " "
				+ node.getEstimatedPosition().yCoord + " "
				+ node.getPosition().distanceTo(node.getEstimatedPosition()));
	}

	public void onExit() {
	}

	public static double percentageBeacons() {
		return percentageBeacons;
	}

	public static int minReferences() {
		return minReferences;
	}

	public static int maxReferences() {
		return maxReferences;
	}

	public static double maxResidual() {
		return maxResidual;
	}

	public static double noiseMean() {
		return noiseMean;
	}

	public static double noiseVariance() {
		return noiseVariance;
	}

	public static void setTerminated(boolean t) {
		terminated = t;
	}

	public static boolean getTerminated() {
		return terminated;
	}

	private static Position[] recursionOriginForOneBeaconsStructure(
			double xmin, double xmax, double ymin, double ymax) {
		Position[] points = new Position[1];
		double cx = (xmax + xmin) / 2;
		double cy = (ymax + ymin) / 2;
		points[0] = new Position(cx, cy, 0);
		return points;
	}

	private static Position[] recursionOriginForTwoBeaconsStructures(
			double xmin, double xmax, double ymin, double ymax) {
		Position[] points = new Position[2];
		double divX = (xmax + xmin) / 2;
		points[0] = recursionOriginForOneBeaconsStructure(xmin, divX, ymin,
				ymax)[0];
		points[1] = recursionOriginForOneBeaconsStructure(divX, xmax, ymin,
				ymax)[0];
		return points;
	}

	private static Position[] recursionOriginForThreeBeaconsStructures(
			double xmin, double xmax, double ymin, double ymax) {
		Position[] points = new Position[3];
		points[0] = recursionOriginForOneBeaconsStructure(xmin, xmax, ymin,
				ymax)[0];
		points[1] = recursionOriginForTwoBeaconsStructures(xmin, xmax, ymin,
				ymax)[0];
		points[2] = recursionOriginForTwoBeaconsStructures(xmin, xmax, ymin,
				ymax)[1];
		return points;
	}

	private static Position[] recursionOriginForFourBeaconsStructures(
			double xmin, double xmax, double ymin, double ymax) {
		Position[] points = new Position[4];
		double divX = (xmax + xmin) / 2;
		double divY = (ymax + ymin) / 2;
		points[0] = recursionOriginForOneBeaconsStructure(xmin, divX, ymin,
				divY)[0];
		points[1] = recursionOriginForOneBeaconsStructure(xmin, divX, divY,
				ymax)[0];
		points[2] = recursionOriginForOneBeaconsStructure(divX, xmax, ymin,
				divY)[0];
		points[3] = recursionOriginForOneBeaconsStructure(divX, xmax, divY,
				ymax)[0];
		return points;
	}

	private static Position[] recursionOriginForFiveBeaconsStructures(
			double xmin, double xmax, double ymin, double ymax) {
		Position[] points = new Position[5];
		Position[] points4 = recursionOriginForFourBeaconsStructures(xmin,
				xmax, ymin, ymax);
		points[0] = recursionOriginForOneBeaconsStructure(xmin, xmax, ymin,
				ymax)[0];
		points[1] = points4[0];
		points[2] = points4[1];
		points[3] = points4[2];
		points[4] = points4[3];
		return points;
	}

	private static int beaconsStructureDistanceFromCentralPoint() {
		return beaconsStructureDistanceFromCentralPoint;
	}

	public static String getLocalizationAlgorithm() {
		return localizationAlgorithm;
	}

	public void customPaint(Graphics g, PositionTransformation pt) {
		Node n = Tools.getNodeList().iterator().next();
		int drawingSizeInPixels = (int) (n.getDefaultDrawingSizeInPixels() * pt
				.getZoomFactor());
		if (targetEnabled) {
			if (paintTrace) {
				for (Position pos : target.getTrace()) {
					pt.translateToGUIPosition(pos);

					int x = pt.guiX - (drawingSizeInPixels >> 1);
					int y = pt.guiY - (drawingSizeInPixels >> 1);
					Color color = Color.CYAN;
					g.setColor(color);
					g.fillRect(x, y, drawingSizeInPixels, drawingSizeInPixels);
					g.setColor(color);
				}
			} else {
				pt.translateToGUIPosition(target.getPosition());
				int x = pt.guiX - (drawingSizeInPixels >> 1);
				int y = pt.guiY - (drawingSizeInPixels >> 1);
				Color color = Color.CYAN;
				g.setColor(color);
				g.fillRect(x, y, drawingSizeInPixels, drawingSizeInPixels);
				g.setColor(color);
			}
		}

	}

	public void preRound() {
		target.updatePosition();
	}

}